#include "Epoll.h"

using namespace std;

Epoll::Epoll()
{
    // 创建epoll句柄。(红黑树)
    epollfd_ = epoll_create(1);
    if(epollfd_ == -1)
    {
        cout << "epoll_create failed" << endl;
        exit(-1);
    }
}

Epoll::~Epoll()
{
    close(epollfd_);
}

void Epoll::updateChannel(Channel *ch)
{
    // 声明事件的数据结构
    epoll_event ev;
    // 指定channel
    ev.data.ptr = ch;
    // 指定事件
    ev.events = ch->events();

    // 如果channel已经在树上了
    if(ch->inpoll())
    {
        if(epoll_ctl(epollfd_, EPOLL_CTL_MOD, ch->fd(), &ev) == -1)
        {
            cout << "epoll_ctl failed" << endl;
            exit(-1);
        }
    }
    // 如果channel不在树上
    else
    {
        if(epoll_ctl(epollfd_, EPOLL_CTL_ADD, ch->fd(), &ev) == -1)
        {
            cout << "epoll_ctl failed" << endl;
            exit(-1);
        }
        // 设置channel在树上了
        ch->setinepoll();
    }
}

void Epoll::removeChannel(Channel *ch)
{
    // 如果channel已经在树上了
    if(ch->inpoll())
    {
        if(epoll_ctl(epollfd_, EPOLL_CTL_DEL, ch->fd(), 0) == -1)
        {
            cout << "epoll_ctl failed" << endl;
            exit(-1);
        }
    }
}

std::vector<Channel*> Epoll::loop(int timeout)
{
    // 存放 epoll_wati() 返回的事件
    std::vector<Channel*> channels;

    // 清空数据
    bzero(events_, sizeof(events_));

    // 等待监视的socket有事件发生
    int infds = epoll_wait(epollfd_, events_, MaxEvents, timeout);

    // 返回失败。
    if (infds < 0)
    {
        /* 
        失败有四种情况：
        EBADF：epfd不是一个有效的描述符
        EFAULT：参数events指向的内存区域不可写
        EINVAL：epfd不是一个epooll文件描述符，或者参数maxevetns小于等于0
        EINTR：阻塞过程中被信号中断，epoll_wait可以避免，或者错误处理中，解析error=EINTR后重新调用epoll_wait
        前三种是代码的bug，不是逻辑漏洞，故不需要通知TcpServer，
        第四种设计到了信号，在Reactor模型不推荐使用信号，因为信号处理起来很麻烦，没有必要 —— 陈硕【moudo库作者】
        故这里的错误不需要通知TcpServer
        */
        perror("epoll() failed");
        exit(-1);
    }

    // 超时。
    if (infds == 0)
    {
        // 如果超时，代表服务器很空闲，需要通知TcpServer
        return channels;
    }

    // infds > 0,代表有事件发生
    for(int i = 0; i < infds; i++)
    {
        Channel *ch = (Channel*)events_[i].data.ptr;
        ch->setrevents(events_[i].events);
        channels.push_back(ch);
    }

    return channels;
}
